#!/usr/bin/python3
# ******************************************************************************
# Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
# licensed under the Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#     http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
# PURPOSE.
# See the Mulan PSL v2 for more details.
# Author: DisNight
# Create: 2020-12-28
# ******************************************************************************/
"""
Method about dnf and method of obtaining repolist
"""

import os
import subprocess
import logging

logging.getLogger().setLevel(logging.INFO)

def call_dnf_subprocess(cmd):
    """
    call_dnf_subprocess
    Args:
        cmd: cmd

    Returns:

    """
    try:
        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
    except OSError as error:
        logging.error("oserror: %s", str(error))
        return None, None
    stdout, stderr = proc.communicate()
    returncode = proc.returncode
    proc.poll()
    return stdout.decode("utf-8", "ignore") + stderr.decode("utf-8", "ignore"), returncode


def generate_dnf_repo_condition(repo_list):
    """
    generate dnf repo condition
    Args:
        repo_list: repo_list

    Returns:
        repo_cmd
    """
    if not repo_list:
        return ""
    repo_cmd = ""
    for repo in repo_list:
        repo_cmd += "--repo {repo} ".format(repo=repo)
    return repo_cmd


def generate_dnf_repo_file_condition(repo_file):
    """
    generate dnf repo file condition
    Args:
        repo_file: repo_file

    Returns:
        repo_file
    """
    if not repo_file or not os.path.exists(repo_file):
        return ""
    return " -c {} ".format(repo_file)


def dnf_list(save_file, path=None, repos=None, repo_file=None):
    """
    get rpm lists in repo by rpm list
    Args:
        save_file: save_file
        path: path
        repos: repos
        repo_file: repo_file

    Returns:
        result_path: result_path
    """
    work_dir = path
    if not work_dir or (os.path.exists(work_dir) and os.path.isdir(work_dir)):
        work_dir = os.path.dirname(os.path.realpath(__file__))
    result_path = os.path.join(work_dir, save_file)
    repo_condition = generate_dnf_repo_condition(repos)
    repo_file_condition = generate_dnf_repo_file_condition(repo_file)
    cmd = "dnf list {rf} {repo} > {path}" \
        .format(rf=repo_file_condition, repo=repo_condition, path=result_path)
    call_dnf_subprocess(cmd)
    return result_path


def dnf_list_src_version(pkg_name, repos=None, repo_file=None):
    """
    use command [dnf list (--repo repo_name)* | grep pkg_name.src] to get [e:v-r] format version
    return epoch, version, release
    Args:
        pkg_name: pkg_name
        repos: repos
        repo_file: repo_file

    Returns:
        epoch: epoch
        version: version
        release: release
    """
    repo_condition = generate_dnf_repo_condition(repos)
    repo_file_condition = generate_dnf_repo_file_condition(repo_file)
    cmd = "dnf list {rf} {repo} | grep {name}.src" \
        .format(rf=repo_file_condition, repo=repo_condition, name=pkg_name)
    stdout, returncode = call_dnf_subprocess(cmd)
    version = ""
    release = ""
    epoch = "0"
    for line in stdout.splitlines(False):
        if not line:
            continue
        pkg_info = line.split()
        if len(pkg_info) != 3:
            continue
        ver_info = pkg_info[1].rsplit(".", 1)[0]
        if ":" in ver_info:
            ver_tmp_list = ver_info.split(":", 1)
            epoch = ver_tmp_list[0]
            ver_info = ver_tmp_list[-1]
        vr_list = ver_info.rsplit("-", 1)
        version = vr_list[0]
        release = vr_list[-1]
        break
    return epoch, version, release


def dnf_download(down_dir, bin_name, repos=None, repo_file=None):
    """
    download roms by dnf download need upgrade yum
    Args:
        down_dir: down_dir
        bin_name: bin_name
        repos: repos
        repo_file: repo_file

    Returns:
        call_dnf_subprocess(cmd)
    """
    current_dir = down_dir if down_dir else os.path.dirname(os.path.realpath(__file__))
    _ = not os.path.exists(current_dir) and os.makedirs(current_dir)
    repo_condition = generate_dnf_repo_condition(repos)
    repo_file_condition = generate_dnf_repo_file_condition(repo_file)
    cmd = "dnf download {rf} {bin} --downloaddir={ddir} {repo}" \
        .format(rf=repo_file_condition, bin=bin_name, ddir=current_dir, repo=repo_condition)
    return call_dnf_subprocess(cmd)


def dnf_info_src(bin_name, repos=None, repo_file=None):
    """
    grep dnf info source field to get src name of bin rpm
    Args:
        bin_name: bin_name
        repos: repos
        repo_file: repo_file

    Returns:
        call_dnf_subprocess(cmd)
    """
    repo_condition = generate_dnf_repo_condition(repos)
    repo_file_condition = generate_dnf_repo_file_condition(repo_file)
    cmd = "dnf info {rf} {bin} {repo} | grep Source" \
        .format(rf=repo_file_condition, bin=bin_name, repo=repo_condition)
    return call_dnf_subprocess(cmd)


def dnf_info_name(bin_name, repos=None, repo_file=None):
    """
    grep dnf info name field to get bin rpm name
    Args:
        bin_name: bin_name
        repos: repos
        repo_file: repo_file

    Returns:
        call_dnf_subprocess(cmd)
    """
    repo_condition = generate_dnf_repo_condition(repos)
    repo_file_condition = generate_dnf_repo_file_condition(repo_file)
    cmd = "dnf info {rf} {bin} {repo} | grep Name" \
        .format(rf=repo_file_condition, bin=bin_name, repo=repo_condition)
    return call_dnf_subprocess(cmd)


def find_what_requires(bin_name, repos=None, repo_file=None):
    """
    find install bedepended of bin rpm
    Args:
        bin_name: bin_name
        repos: repos
        repo_file: repo_file

    Returns:
        list(result_set)
    """
    repo_condition = generate_dnf_repo_condition(repos)
    repo_file_condition = generate_dnf_repo_file_condition(repo_file)
    cmd = "dnf repoquery {rf} --nvr --whatrequires {bin} {repo}" \
        .format(rf=repo_file_condition, bin=bin_name, repo=repo_condition)
    stdout, returncode = call_dnf_subprocess(cmd)
    data = stdout.split("\n")
    result_set = set()
    flag = True
    for element in data:
        if not element.strip():
            continue
        name = element.strip()
        result_set.add(name)
    return list(result_set)


def find_update_install_rpms(pkg_name, rpm_dir, repos=None, repo_file=None):
    """
    find all install requires which need update
    Args:
        pkg_name: pkg_name
        rpm_dir: rpm_dir
        repos: repos
        repo_file: repo_file

    Returns:
        result
    """
    if not os.path.isdir(rpm_dir) or not repos:
        return None
    install_rpms = os.path.abspath(rpm_dir) + "/*"
    repo_name = repos[0]
    repo_file_condition = generate_dnf_repo_file_condition(repo_file)
    cmd = "dnf install {rf} {rpms} --assumeno | grep {rn}" \
        .format(rf=repo_file_condition, rpms=install_rpms, rn=repo_name)
    stdout, returncode = call_dnf_subprocess(cmd)
    data = stdout.split("\n")
    result = set()
    logging.info("finding %s installed requires", pkg_name)
    for line in data:
        if not repo_name in line:
            continue
        bin_name = line.strip().split()[0]
        logging.info("[%s] install require [%s] need update", pkg_name, bin_name)
        result.add(bin_name)
    return result


def is_dnf_install_succeed(pkg_name, repos=None, repo_file=None):
    """
    use dnf install pkg --assumeno to judeg repos' complete
    Args:
        pkg_name: pkg_name
        repos: repos
        repo_file: repo_file

    Returns:
        True or False
    """
    repo_condition = generate_dnf_repo_condition(repos)
    repo_file_condition = generate_dnf_repo_file_condition(repo_file)
    cmd = "dnf install {rf} {pkg} --assumeno {repos}" \
        .format(rf=repo_file_condition, pkg=pkg_name, repos=repo_condition)
    out, code = call_dnf_subprocess(cmd)
    if "Dependencies resolved" in out:
        return True
    return False


def get_all_repolist(repo_file=None):
    """
    get all set repo in /etc/yum.repo.d/*.repo
    Args:
        repo_file: repo_file

    Returns:
        result: result
    """

    repo_file_condition = generate_dnf_repo_file_condition(repo_file)
    cmd = "dnf repolist {rf} --all".format(rf=repo_file_condition)
    stdout, returncode = call_dnf_subprocess(cmd)
    data = stdout.split("\n")
    result = set()
    if len(data) > 1:
        for line in data[1:]:
            if not line.strip():
                continue
            result.add(line.strip().split()[0].strip())
    return result
